package com.bjsxt.search.dao.impl;

import com.bjsxt.search.dao.ItemDao4ES;
import com.bjsxt.search.pojo.Item4ES;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * @ClassName : ItemDao4ESImpl
 * @Description :
 * @Author : ZWZ
 * @Date: 2021/8/24 16:48
 */
@Repository
public class ItemDao4ESImpl implements ItemDao4ES {

    @Value("${livegoods.search.init.enabled}")
    private Boolean initEnabled;
    @Autowired
    private ElasticsearchRestTemplate restTemplate;


    /**
     * 分页搜索， 高亮处理
     *
     * @param city    城市位置
     * @param content 搜索关键字， 在 title 商品标题字段中匹配。
     * @param page    第几页， 页面从 0 页开始
     * @param size    每页的数量
     * @return
     */
    @Override
    public AggregatedPage<Item4ES> queryForPage(String city, String content, int page, int size) {
        // 搜索 title 高亮处理
        HighlightBuilder.Field highlightTitle = new HighlightBuilder.Field("title");
        highlightTitle.preTags("<span style='color:red;'>");
        highlightTitle.postTags("</span>");

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        // 获取必选查询条件
        List<QueryBuilder> mustList = boolQueryBuilder.must();
        mustList.add(QueryBuilders.matchQuery("city", city));

        // 获取可选查询条件
        List<QueryBuilder> shouldList = boolQueryBuilder.should();
        // 标题搜索
        shouldList.add(QueryBuilders.matchQuery("title", content));
        // 房屋类型搜索
        shouldList.add(QueryBuilders.matchQuery("houseType", content));
        // 租赁方式搜索
        shouldList.add(QueryBuilders.matchQuery("rentType", content));

        NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
                .withQuery(boolQueryBuilder)  // 搜索条件
                .withHighlightFields(highlightTitle)  // 高亮
                .withPageable(PageRequest.of(page, size))  // 分页
                .build();

        // 搜索
        AggregatedPage<Item4ES> restPageResult = restTemplate.queryForPage(nativeSearchQuery, Item4ES.class, new SearchResultMapper() {

            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> aClass, Pageable pageable) {
                // 存放搜索结果
                List<T> resultList = new ArrayList<>();

                for (SearchHit searchHit : response.getHits().getHits()) {
                    // 获取搜索结果
                    Map<String, Object> sourceAsMap = searchHit.getSourceAsMap();
                    Item4ES item4ES = new Item4ES();
                    // 数据处理
                    item4ES.setId(sourceAsMap.get("id").toString());
                    item4ES.setRentType(sourceAsMap.get("rentType").toString());
                    item4ES.setPrice(sourceAsMap.get("price").toString());
                    item4ES.setImg(sourceAsMap.get("img").toString());
                    item4ES.setHouseType(sourceAsMap.get("houseType").toString());
                    item4ES.setCity(sourceAsMap.get("city").toString());
                    // 处理高亮
                    Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
                    if (highlightFields.containsKey("title")) {
                        item4ES.setTitle(highlightFields.get("title").getFragments()[0].toString());
                    } else {
                        item4ES.setTitle(sourceAsMap.get("title").toString());
                    }
                    resultList.add((T) item4ES);
                }

                return new AggregatedPageImpl<>(
                        resultList, // 搜索结果结合
                        pageable,  // 分页辅助对象
                        response.getHits().getTotalHits(), //总计多少行数据，常规返回时，前三个构造参数就足够了
                        response.getAggregations(),
                        response.getScrollId(), // 窗口搜索主键
                        response.getHits().getMaxScore() // 搜索结果的最大分 数
                );
            }

            @Override
            public <T> T mapSearchHit(SearchHit searchHit, Class<T> aClass) {
                return null;
            }
        });

        return restPageResult;
    }

    @Override
    public void batchIndex(List<Item4ES> items) {
        // 判断是否需要创建索引和设置映射。
        if (initEnabled) {
            this.createIndex();
            this.putMapping();
        }

        List<IndexQuery> queryList = new ArrayList<>();
        for (Item4ES item : items) {
            queryList.add(new IndexQueryBuilder().withObject(item).build());
        }
        // 批量新增
        restTemplate.bulkIndex(queryList);
    }

    // 创建索引
    private void createIndex() {
        restTemplate.createIndex(Item4ES.class);
    }

    // 创建索引映射
    private void putMapping() {
        restTemplate.putMapping(Item4ES.class);
    }

    @Override
    public void index(Item4ES item) {
        this.batchIndex(Arrays.asList(item));
    }

}
